package com.ttxs.fileformathandling.utils;

import com.opencsv.CSVReader;
import com.opencsv.exceptions.CsvValidationException;
import com.ttxs.fileformathandling.common.ErrorCode;
import com.ttxs.fileformathandling.exception.BusinessException;
import com.ttxs.fileformathandling.model.dto.userdatastructure.SimpleQuery;
import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVParser;
import org.apache.commons.csv.CSVPrinter;
import org.apache.commons.csv.CSVRecord;
import org.apache.commons.text.StringEscapeUtils;

import java.io.*;
import java.util.ArrayList;
import java.util.List;


/**
 * CSV工具类   格式转换、格式验证、格式化功能
 *
 * @author xsb
 */

public class CsvUtils {

    private static void begin(String url) {
        if (!csvValidator(url, ",")) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "CSV文件格式错误");
        }
    }

    /***
     *实现CSV转JSON
     * @param url 文件地址
     * @param ourl 输出文件地址
     * @return 返回转化好的json字符串
     * @author xsb
     */

    public static void csv2Json(String url, String ourl) {
        begin(url);
        int hang = 0;    //行数容器
        StringBuilder strJ;    //json文件容器
        String[] strName = new String[0];    //属性名容器
        String[] strC;

        CSVReader reader = null;
        try {
            reader = new CSVReader(new FileReader(url));
        } catch (FileNotFoundException e) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "文件不存在");
        }
        File t = new File(ourl);    //写入文件，若没有则创建
        FileWriter fw = null;
        try {
            fw = new FileWriter(t, true);
            fw.write("[\n");
        } catch (IOException e) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "文件写入失败");
        }
        boolean flag = false;
        while (true) {
            try {
                strJ = new StringBuilder();
                strC = reader.readNext();
                if (strC == null) break;   //没有下一行，退出
                hang++;    //行数自增
                if (hang == 1) {    //第一行是属性名,属性名应该做标签
                    strName = strC.clone();    //把属性名拿出来
                    continue;
                }
                for (int k = 0; k < strName.length; k++) {
                    if (!(strName[k].startsWith("\"") && strName[k].endsWith("\""))) {
                        strName[k] = "\"" + strName[k] + "\"";    //给没引号包裹的加上引号
                    }
                }
                int i = 0;
                strJ.append("\t{\n\t\t");    //一组参数头的缩进与大括号并换行和下一行缩进
                while (i < strName.length) {

                    try {
                        // 尝试将字符串转换为浮点数
                        Double.parseDouble(strC[i]);
                    } catch (NumberFormatException e1) {    //转换失败加引号
                        flag = true;
                    }

                    if ("true".equalsIgnoreCase(strC[i])) {
                        flag = false;
                        strC[i] = "true"; //json中只能识别小写的 true false
                    }
                    if ("false".equalsIgnoreCase(strC[i])) {
                        flag = false;
                        strC[i] = "false";
                    }
                    if (flag) {    //有引号不转换
                        if (strC[i].startsWith("\"") && strC[i].endsWith("\"")) {
                            continue;
                        } else {
                            strC[i] = "\"" + strC[i] + "\"";    //给没引号包裹的加上引号
                        }
                        flag = false;
                    }
                    strJ.append(strName[i].trim()).append(" : ").append(strC[i].trim());
                    if (i == strName.length - 1) {
                        strJ.append("\n");    //一组参数里最后一个参数，不要逗号，换行
                    } else {
                        strJ.append(",\n\t\t");    //逗号分割同一组的不同参数并换行和下一行缩进
                    }
                    i++;
                }
                strJ.append("\t},\n");    //一组参数结尾的大括号和逗号并换行
                fw.write(String.valueOf(strJ));
            } catch (CsvValidationException | IOException e) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "转换失败");
            }
        }
        try {
            reader.close();
            fw.close();
            RandomAccessFile rw = new RandomAccessFile(ourl, "rw");
            rw.setLength(rw.length() - 2);
            rw.close();
        } catch (IOException e) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "文件写入失败");
        }
        if (hang != 0) {    //不是空文件
            FileWriter fwm;
            try {
                fwm = new FileWriter(t, true);
                fwm.write("\n]");
                fwm.close();
            } catch (IOException e) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "文件写入失败");
            }
        }
    }

    /***
     *实现CSV转XML
     * @param url 文件地址
     * @param ourl 输出文件地址
     * @return 返回转化好的xml字符串
     * @author xsb
     */

    public static void csv2Xml(String url, String ourl) {
        begin(url);
        int hang = 0;    //行数容器
        StringBuilder strJ;    //json文件容器
        String[] strName = new String[0];    //属性名容器
        String[] strC;

        CSVReader reader = null;
        try {
            reader = new CSVReader(new FileReader(url));
        } catch (FileNotFoundException e) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "文件不存在");
        }
        File t = new File(ourl);    //写入文件，若没有则创建
        FileWriter fw = null;
        try {
            fw = new FileWriter(t, true);
            fw.write("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");  //文件头，换行
            fw.write("<dataset>\n");  //全部数据的开始标签，换行
        } catch (IOException e) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "文件写入失败");
        }
        while (true) {
            try {
                strJ = new StringBuilder();
                strC = reader.readNext();
                if (strC == null) break;
                hang++;    //行数自增
                if (hang == 1) {    //第一行是属性名,属性名应该做标签
                    strName = strC.clone();    //把属性名拿出来
                    continue;
                }
                int i = 0;
                strJ.append("\t<record>\n");    //缩进，一组数据的开始标签，换行
                while (i < strName.length) {
                    //xml中标签只能包含 字母，数字，冒号，连字符，下划线
                    String tag = strName[i].replaceAll("[^\\w:-]", "_");
                    //xml中值 中如果包含 <  &  则需要进行转义
                    String val = StringEscapeUtils.escapeXml10(strC[i]);
                    strJ.append("\t\t<").append(tag).append(">").append(val).append("</").append(tag).append(">\n");
                    i++;
                }
                strJ.append("\t</record>\n");    //缩进，一组数据的结束标签，换行
                fw.write(String.valueOf(strJ));
            } catch (CsvValidationException | IOException e) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "转换失败");
            }
        }
        try {
            reader.close();
            fw.write("</dataset>");    //全部数据的结束标签
            fw.close();
        } catch (IOException e) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "文件写入失败");
        }
    }


    /***
     *实现JSON转XML
     * @param url 文件地址
     * @param ourl 输出文件地址
     * @return 返回转化好的yaml字符串
     * @author xsb
     */

    public static void csv2Yaml(String url, String ourl) {
        begin(url);
        int hang = 0;    //行数容器
        StringBuilder strJ;    //json文件容器
        String[] strName = new String[0];    //属性名容器
        String[] strC;

        CSVReader reader = null;
        try {
            reader = new CSVReader(new FileReader(url));
        } catch (FileNotFoundException e) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "文件不存在");
        }
        File t = new File(ourl);    //写入文件，若没有则创建
        FileWriter fw = null;
        try {
            fw = new FileWriter(t, true);
        } catch (IOException e) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "文件写入失败");
        }
        while (true) {
            try {
                strJ = new StringBuilder();
                strC = reader.readNext();
                if (strC == null) break;
                hang++;    //行数自增
                if (hang == 1) {    //第一行是属性名,属性名应该做标签
                    strName = strC.clone();    //把属性名拿出来
                    continue;
                }
                int i = 0;
                strJ.append("-");    //一组数据的头
                while (i < strName.length) {
                    if (i == 0) {
                        strJ.append(" ");
                    } else {
                        strJ.append("  ");
                    }
                    strJ.append(strName[i]).append(": ").append(strC[i]).append("\n");    //缩进，键值对key: value，换行
                    i++;
                }
                fw.write(String.valueOf(strJ));
            } catch (CsvValidationException | IOException e) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "转换失败");
            }
        }
        try {
            reader.close();
            fw.close();
        } catch (IOException e) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "文件写入失败");
        }
    }

    /***
     *实现CSV格式验证
     * @param url 文件地址
     * @param gt 分隔符
     * @author xsb
     */

    public static boolean csvValidator(String url, String gt) {
        try (CSVReader reader = new CSVReader(new FileReader(url))) {
            String[] headers = reader.readNext();
            if (headers == null || headers.length == 0) {
//                System.out.println("CSV文件头部为空.");
                return false;
            }

            int expectedNumColumns = headers.length;

            String[] record;
            while ((record = reader.readNext()) != null) {
                if (record.length != expectedNumColumns) {

//                    System.out.println("CSV文件的行 " + reader.getLinesRead() + " 列数与头部不匹配.");
                    return false;
                }
            }

            return true;
        } catch (IOException | CsvValidationException e) {
            return false;
        }

    }


    public static String[] csvStandard(String inputPath, String outputPath, String[] headers, boolean append, boolean removeHead) {
        try (CSVReader reader = new CSVReader(new FileReader(inputPath));
             FileWriter writer = new FileWriter(outputPath, append)) {
            if (headers == null || headers.length == 0) {
                headers = reader.readNext();
                if (!removeHead) {
                    writer.write(String.join(",", headers));
                    writer.write(System.lineSeparator());
                }
            } else if (removeHead) {
                reader.readNext();
            }

            int expectedNumColumns = headers.length;

            String[] record;
            while ((record = reader.readNext()) != null) {
                if (record.length > expectedNumColumns) {
                    throw new BusinessException(ErrorCode.PARAMS_ERROR, "CSV文件的第" + reader.getLinesRead() + "行列数与头部不匹配.");
                } else if (record.length < expectedNumColumns) {
                    //将该行内容写入outputPath并补全缺少的,
                    String[] newRecord = new String[expectedNumColumns];
                    System.arraycopy(record, 0, newRecord, 0, record.length);
                    for (int i = record.length; i < expectedNumColumns; i++) {
                        newRecord[i] = "";
                    }
                    writer.write(String.join(",", newRecord));
                    writer.write(System.lineSeparator());
                } else {
                    //直接将该行内容写入outputPath
                    writer.write(String.join(",", record));
                    writer.write(System.lineSeparator());
                }
            }
            return headers;
        } catch (IOException | CsvValidationException e) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR);
        }

    }

    public static String csvQuery(SimpleQuery simpleQuery, String inputPath) {
        StringBuilder sb = new StringBuilder();
        try (CSVReader reader = new CSVReader(new FileReader(inputPath));
        ) {
            int left = simpleQuery.getLeft();
            int right = simpleQuery.getRight();
            int rowCount = 1;
            while (rowCount < left) {
                reader.readNext();
                rowCount++;
            }
            String[] record;
            while (((record = reader.readNext()) != null) && (rowCount <= right)) {
                rowCount++;
                for (Integer index : simpleQuery.getIndex()) {
                    sb.append(record[index]);
                    sb.append(",");
                }
                sb.deleteCharAt(sb.length() - 1);
                sb.append("\n");
            }
            return sb.toString();
        } catch (IOException | CsvValidationException e) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR);
        }
    }

    /***
     *实现CSV格式规范
     * @param url 文件地址
     * @param ourl 输出文件地址
     * @author xsb
     */

    public static void csvFormatter(String url, String ourl) {
        begin(url);
        // 设置 CSV 格式
        CSVFormat csvFormat = CSVFormat.RFC4180.withFirstRecordAsHeader();

        // 设置输入文件和输出文件
        File inputFile = new File(url);
        File outputFile = new File(ourl);

        // 设置输入文件和输出文件的流
        try (Reader reader = new BufferedReader(new FileReader(inputFile));
             Writer writer = new BufferedWriter(new FileWriter(outputFile));
             CSVParser csvParser = new CSVParser(reader, csvFormat);
             CSVPrinter csvPrinter = new CSVPrinter(writer, csvFormat)) {

            // 处理表头
            String[] s_head = csvParser.getHeaderMap().keySet().toString().replace("[", "").replace("]", "").split(",");
            int j = 0;
            while (j < s_head.length) {
                s_head[j] = s_head[j].trim();
                j++;
            }
            csvPrinter.printRecord(s_head);

            // 处理数据行
            List<CSVRecord> records = csvParser.getRecords();
            for (int rowNumber = 0; rowNumber < records.size(); rowNumber++) {
                CSVRecord record = records.get(rowNumber);
                List<String> normalizedRow = new ArrayList<>();
                // 处理每个单元格
                for (int i = 0; i < record.size(); i++) {
                    String cellValue = record.get(i);
                    if (cellValue == null || cellValue.isEmpty()) {
                        normalizedRow.add("null");
                    } else {
                        normalizedRow.add(cellValue.trim());
                    }
                }
                while (normalizedRow.size() != csvParser.getHeaderMap().size()) {
                    normalizedRow.add("null");
                }
                csvPrinter.printRecord(normalizedRow);
            }
        } catch (IOException e) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "CSV格式化失败");
        }
    }
}